import { Injectable } from '@nestjs/common';
import { eq, isNull, asc } from 'drizzle-orm';
import { sysMenu, sysRoleMenu, BaseService, db, type SysMenu } from '@nbase/db';
import { BizException } from '@nbase/nest-mods';
import type { IMenu, IMenuTree, IPaginationResponse } from '@nbase/shared';
import { CreateMenuDto } from './dto/create-menu.dto';
import { UpdateMenuDto } from './dto/update-menu.dto';
import { QueryMenuDto } from './dto/query-menu.dto';
import { toMenu, toMenuPage } from './menu.mapper';

/**
 * 菜单服务
 */
@Injectable()
export class MenuService extends BaseService<typeof sysMenu> {
  constructor() {
    super(sysMenu);
  }

  /**
   * 创建菜单
   */
  async createMenu(createMenuDto: CreateMenuDto): Promise<IMenu> {
    // 检查 permission 是否已存在
    const isExist = await this.exists(eq(sysMenu.permission, createMenuDto.permission));
    if (isExist) {
      throw BizException.menuExist();
    }

    // 如果有父级菜单，检查是否存在
    if (createMenuDto.parentId) {
      const parent = await this.findById(createMenuDto.parentId);
      if (!parent) {
        throw BizException.menuNotFound('父级菜单不存在');
      }
    }

    const created = await this.create(createMenuDto);
    return toMenu(created as SysMenu);
  }

  /**
   * 更新菜单
   */
  async updateMenu(id: string, updateMenuDto: UpdateMenuDto): Promise<IMenu> {
    // 如果更新 permission，检查是否已存在
    if (updateMenuDto.permission) {
      const existing = await this.findOne(eq(sysMenu.permission, updateMenuDto.permission));
      if (existing && existing.id !== id) {
        throw BizException.menuExist();
      }
    }

    // 如果有父级菜单，检查是否存在
    if (updateMenuDto.parentId) {
      const parent = await this.findById(updateMenuDto.parentId);
      if (!parent) {
        throw BizException.menuNotFound('父级菜单不存在');
      }

      // 不能将菜单设置为自己的子菜单
      if (updateMenuDto.parentId === id) {
        throw BizException.invalidParams('不能将菜单设置为自己的子菜单');
      }
    }

    const updated = await this.update(id, updateMenuDto);
    return toMenu(updated as SysMenu);
  }

  /**
   * 删除菜单
   */
  async deleteMenu(id: string): Promise<void> {
    // 检查是否有子菜单
    const hasChildren = await this.exists(eq(sysMenu.parentId, id));
    if (hasChildren) {
      throw BizException.menuHasChildren();
    }

    // 检查是否被角色使用
    const roleMenus = await db
      .select()
      .from(sysRoleMenu)
      .where(eq(sysRoleMenu.menuId, id))
      .limit(1);

    if (roleMenus.length > 0) {
      throw BizException.menuInUse();
    }

    await this.softDelete(id);
  }

  /**
   * 分页查询菜单
   */
  async paginateMenus(query: QueryMenuDto): Promise<IPaginationResponse<IMenu>> {
    const result = await this.paginate(query);
    return toMenuPage(result as IPaginationResponse<SysMenu>);
  }

  /**
   * 获取菜单树
   * 返回树形结构的菜单列表
   */
  async getMenuTree(): Promise<IMenuTree[]> {
    const menus = (await this.findAll()) as SysMenu[];
    const plainMenus = menus.map(toMenu);
    return this.buildTree(plainMenus, null);
  }

  /**
   * 获取根菜单列表
   */
  async getRootMenus(): Promise<IMenu[]> {
    const menus = await this.find(isNull(sysMenu.parentId), {
      orderBy: asc(sysMenu.sort),
    });
    return (menus as SysMenu[]).map(toMenu);
  }

  /**
   * 获取子菜单列表
   */
  async getChildMenus(parentId: string): Promise<IMenu[]> {
    const menus = await this.find(eq(sysMenu.parentId, parentId), {
      orderBy: asc(sysMenu.sort),
    });
    return (menus as SysMenu[]).map(toMenu);
  }

  /**
   * 根据 permission 查找菜单
   */
  async findByPermission(permission: string): Promise<IMenu | null> {
    const menu = (await this.findOne(eq(sysMenu.permission, permission))) as SysMenu | undefined;
    return menu ? toMenu(menu) : null;
  }

  /**
   * 构建树形结构
   */
  private buildTree(menus: IMenu[], parentId: string | null): IMenuTree[] {
    return menus
      .filter((menu) => menu.parentId === parentId)
      .map((menu) => ({
        ...menu,
        children: this.buildTree(menus, menu.id),
      }));
  }
}

